#include "AllHeadFile.h"

struct_pos pos_now = {0, 0};    //初始化光标
I2C_HandleTypeDef *i2c = NULL;  //I2C句柄指针
// ``` !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~```    开头有个空格呦~

//初始化指令集(长度33)，78 00 AE D5 80 A8 3F D3 00 40 A1 C8 DA 12 D9 F1 DB 20 81 FF A4 A6 8D 14 20 00 21 00 7F 22 00 07 AF
const uint8_t ssd1315_init_commend[] = {
    0xAE ,   //关闭显示AE
    0xD5 ,0x80 ,//时钟分频，推荐值0x80
    0xA8 ,0x3F ,//Multiplex 路数，标配64也就是0x3f
    0xD3 ,0x00 ,//垂直偏移0
    0x40 ,//设置显示起始行寄存器
    0xA1 ,0xC8 ,//段、行方向，默认横向，还可选A0和C0
    0xDA ,0x12 ,//COM引脚配置
    0xD9 ,0xF1 ,//预充电周期，F1表示相位2是0xf个周期（30 DCLK），相位1是1个周期（1 DCLK）,最大FF。SSD1306常用D9 22.
    0xDB ,0x20 ,//VCOMH取消选择电平,只定义四档：0x00≈0.65、0x10≈0.71、0x20≈0.77(默认)、0x30≈0.83×VCC。
    0x81 ,0xFF ,//对比度（亮度），范围0x00~0xFF，我选的超级无敌大
    0xA4 ,//A4取消屏幕全填充并按照GDDRAM输出，A5进行屏幕全填充并忽略GDDRAM
    0xA6 ,//反转显示，A6是高电平亮低电平灭，A7反之（单个像素）
    0x8D ,0x14 ,//充电泵，7.5V模式（3v3常用）。94是8.5V，95是9V模式
    0x20 ,0x00 ,//00 - 水平寻址，01 - 纵向寻址，10 - 页寻址
    0x21 ,0x00 ,0x7F ,//起始列00，结束列7F
    0x22 ,0x00 ,0x07 ,//起始页00结束页07
    0xAF};//打开显示AF

/**
 *  tips:
 *  若使用CubeMX配置，则可通过修改官方i2c.c文件中的hi2c1.Init.ClockSpeed值超频到850kHz。
 */

//限制区间函数
static int clip(int x, int low, int up) {
    return x > up ? up : x < low ? low : x;
}

//------------------------------------------------------------------------------------------------------
// 函数简介         I2C命令连续写入
// 使用示例         I2C_WriteCommand_Multiple(8, 0x20, 0x00, 0x21, 0x00, 0x7f, 0x22, 0x00, 0x07)
// 参数介绍         count_command           后续数据个数
// 参数介绍         ...                     数据
// 返回参数
// 备注信息         内部调用(手动调用也行)
//------------------------------------------------------------------------------------------------------
void I2C_WriteCommand_Multiple(const uint32_t count_command, ...) {
    va_list cmd;
    va_start(cmd, count_command);
    uint8_t *buff_cmd = malloc(count_command);  //放到堆堆里
    memset(buff_cmd, 0, count_command);         //初始化0
    //循环读取传入指令
    for (uint32_t i = 0; i < count_command; i++) {
        buff_cmd[i] = (uint8_t)va_arg(cmd, int);    //通过...传参会导致“默认参数提升”，char和short升级为int，float升级为double
    }
    HAL_I2C_Mem_Write(i2c,SSD1315_ADDRESS_8BIT,0x00,I2C_MEMADD_SIZE_8BIT, buff_cmd, count_command,0x1000);  //最后是超时时间：似乎100k的i2c要用0x100,1M的要用0x1000（猜的）
    va_end(cmd);
    free(buff_cmd);
}

//------------------------------------------------------------------------------------------------------
// 函数简介         I2C数据连续写入
// 使用示例         I2C_WriteData_Multiple(5, 0x00，0x00, 0x00, 0x00, 0x00)
// 参数介绍         count_data              后续数据个数
// 参数介绍         ...                     数据
// 返回参数
// 备注信息         内部调用(手动调用也行)
//------------------------------------------------------------------------------------------------------
void I2C_WriteData_Multiple(const uint32_t count_data, ...) {
    va_list cmd;
    va_start(cmd, count_data);
    uint8_t *buff_cmd = malloc(count_data);  //放到堆堆里
    memset(buff_cmd, 0, count_data);         //初始化0
    //循环读取传入指令
    for (uint32_t i = 0; i < count_data; i++) {
        buff_cmd[i] = (uint8_t)va_arg(cmd, int);    //通过...传参会导致“默认参数提升”，char和short升级为int，float升级为double
    }
    HAL_I2C_Mem_Write(i2c,SSD1315_ADDRESS_8BIT,0x40,I2C_MEMADD_SIZE_8BIT, buff_cmd, count_data,0x1000);  //似乎100k的i2c要用0x100,1M的要用0x1000（猜的）
    va_end(cmd);
    free(buff_cmd);
}

//------------------------------------------------------------------------------------------------------
// 函数简介         设置显示区域
// 使用示例         OLED_Set_Show_scope(0, 0+31, 0, 0+3);
// 参数介绍         x                   起始横坐标
// 参数介绍         x_end               结束横坐标
// 参数介绍         page                起始页数（一页8行像素）
// 参数介绍         page_end            结束页数
// 返回参数
// 备注信息         内部调用，用户无需关心(关心也行);不包括显示范围钳位，在外围函数里做。
//------------------------------------------------------------------------------------------------------
static void OLED_Set_Show_scope(uint8_t x, uint8_t x_end, uint8_t page, uint8_t page_end) {
    I2C_WriteCommand_Multiple(8, 0x20, 0x00, 0x21, x, x_end, 0x22, page, page_end);//指定显示位置
}

//------------------------------------------------------------------------------------------------------
// 函数简介         OLED初始化
// 使用示例         OLED_Init(&hi2c1);
// 返回参数
// 备注信息         注意在i2C初始化完成后调用
//------------------------------------------------------------------------------------------------------
int OLED_Init(I2C_HandleTypeDef *hi2c) {
    //读取初始化数组来显示
    // for (uint i = 0; i < sizeof(ssd1315_init_commend); i++) {
    //     const uint8_t buf_cmd = ssd1315_init_commend[i];
    //     I2C_WriteCommand_Multiple(1, buf_cmd);
    // }
    i2c = hi2c;

    //如果想快一点就用下面这个
    I2C_WriteCommand_Multiple(31, 0xAE ,   //关闭显示AE
    0xD5 ,0x80 ,//时钟分频，推荐值0x80
    0xA8 ,0x3F ,//Multiplex 路数，标配64也就是0x3f
    0xD3 ,0x00 ,//垂直偏移0
    0x40 ,//设置显示起始行寄存器
    0xA1 ,0xC8 ,//段、行方向，默认横向，还可选A0和C0
    0xDA ,0x12 ,//COM引脚配置
    0xD9 ,0xF1 ,//预充电周期，F1表示相位2是0xf个周期（30 DCLK），相位1是1个周期（1 DCLK）,最大FF。SSD1306常用D9 22.
    0xDB ,0x20 ,//VCOMH取消选择电平,只定义四档：0x00≈0.65、0x10≈0.71、0x20≈0.77(默认)、0x30≈0.83×VCC。
    0x81 ,0xFF ,//对比度（亮度），范围0x00~0xFF，我选的超级无敌大
    0xA4 ,//A4取消屏幕全填充并按照GDDRAM输出，A5进行屏幕全填充并忽略GDDRAM
    0xA6 ,//反转显示，A6是高电平亮低电平灭，A7反之（单个像素）
    0x8D ,0x14 ,//充电泵，7.5V模式（3v3常用）。94是8.5V，95是9V模式
    0x20 ,0x00 ,//水平寻址，01纵向寻址，10页寻址
    0x21 ,0x00 ,0x7F ,//起始列00，结束列7F
    0x22 ,0x00 ,0x07 ,//起始页00结束页07
    0xAF);
    return 0;
}

//------------------------------------------------------------------------------------------------------
// 函数简介         OLED全屏亮
// 使用示例         OLED_ALL_ON();
// 返回参数
// 备注信息
//------------------------------------------------------------------------------------------------------
int OLED_ALL_ON(void) {
    I2C_WriteCommand_Multiple(8, 0x20, 0x00, 0x21, 0x00, 0x7f, 0x22, 0x00, 0x07);   //设定点亮范围
    for (int i = 0; i < 8*8; i++) {
        I2C_WriteData_Multiple(16, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
                               0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff);   //单次点亮一个8*8的小框
    }
    return 0;
}

//------------------------------------------------------------------------------------------------------
// 函数简介         OLED全屏擦除
// 使用示例         OLED_All_Clear();
// 返回参数
// 备注信息
//------------------------------------------------------------------------------------------------------
int OLED_All_Clear(void) {
    //后续改进：使用memset生成一个64*128的0x00数组，一次写入。
    I2C_WriteCommand_Multiple(8, 0x20, 0x00, 0x21, 0x00, 0x7f, 0x22, 0x00, 0x07);   //设定擦除范围
    for (int i = 0; i < 8*8; i++) {
        I2C_WriteData_Multiple(16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);   //单次擦除一个8*8的小框
    }
    return 0;
}

//------------------------------------------------------------------------------------------------------
// 函数简介         OLED部分列擦除
// 使用示例         OLED_Selected_Clear(16, 23, 1, 2);  //消除起点为第二行第16个像素开始的8*16方块儿
// 参数介绍         x                   起始列 0~127
// 参数介绍         end_x               结束列 x~127
// 参数介绍         page                起始页 0~7
// 参数介绍         end_page            结束页 page~7
// 返回参数         int                 0-正常
// 备注信息         注意：清除的最小单位是一列，而不是一个像素（因为没写）
//------------------------------------------------------------------------------------------------------
int OLED_Selected_Clear(uint8_t x, uint8_t end_x, uint8_t page, uint8_t end_page) {
    //传参钳位（uint8_t不会小于0）
    x = (x>SSD1315_WIDTH-1) ? SSD1315_WIDTH-1 : x;
    page = (page>SSD1315_HEIGHT/8-1) ? SSD1315_HEIGHT/8-1 : page;

    I2C_WriteCommand_Multiple(8, 0x20, 0x00, 0x21, x, end_x, 0x22, page, end_page);   //设定擦除范围

    uint8_t scope = 0;
    if (end_x == x && end_page == page)  //设定输出范围
        scope = 1;
    else if (end_x == x)
        scope = (end_page-page)+1;
    else if (end_page == page)
        scope = (end_x-x)+1;
    else
        scope = (end_x-x+1)*(end_page-page+1);
    //利用清楚一次地址自动递增来清除
    for (int i = 0; i < scope; i++) {
        I2C_WriteData_Multiple(1, 0x00);
    }
    return 0;
}

//------------------------------------------------------------------------------------------------------
// 函数简介         OLED显示ASCII字符
// 使用示例         OLED_Show_Char(0, 0, 'a', SIZE_6x8, 0);
// 参数介绍         x                   显示横坐标
// 参数介绍         page                显示页数
// 参数介绍         chr                 待显示字符
// 参数介绍         char_height         显示字符的高度，SIZE_6x8为6*8,SIZE_8x16为8*16,SIZE_24x48为24*48
// 参数介绍         char_turn           反转显示，0不反转，1反转
// 返回参数         (int)               0-正常 -1-异常
// 备注信息
//------------------------------------------------------------------------------------------------------
int OLED_Show_Char(uint8_t x, uint8_t page, uint8_t chr, font_size_t char_height, uint8_t char_turn) {
    x = clip(x, 0, SSD1315_WIDTH-1);            //钳位
    page = clip(page, 0, SSD1315_HEIGHT/8-1);
    chr -= ' ';//得到偏移后的值，因为字母表是从' '开始的，' '的值是32
    // if (char_turn == 0)
    //     char_turn = 0xA6;        //这个是全屏反转，并不是单独某块儿显示反转
    // else if (char_turn == 1)
    //     char_turn = 0xA7;
    if (char_height % 8 !=0) char_height = SIZE_6x8;   //错误输入直接默认为6x8

    if (char_height == SIZE_6x8) {
        if (x+6 > SSD1315_WIDTH-1) return -1;
        OLED_Set_Show_scope(x, x+5, page, page);//指定显示位置
        if (!char_turn)
            I2C_WriteData_Multiple(6, F6x8[chr][0], F6x8[chr][1], F6x8[chr][2], F6x8[chr][3], F6x8[chr][4], F6x8[chr][5]);    //显示内容
        else
            I2C_WriteData_Multiple(6, ~F6x8[chr][0], ~F6x8[chr][1], ~F6x8[chr][2], ~F6x8[chr][3], ~F6x8[chr][4], ~F6x8[chr][5]);    //按位逆
        return 0;
    }
    if (char_height == SIZE_8x16) {
        if (x+8 > SSD1315_WIDTH-1 || page+1 > SSD1315_HEIGHT/8-1) return -1;
        OLED_Set_Show_scope(x, x+7, page, page+1);//指定显示位置，注意8x16的字号会占据两个page
        if (!char_turn)
            I2C_WriteData_Multiple(16, F8X16[chr*16], F8X16[chr*16+1], F8X16[chr*16+2], F8X16[chr*16+3], F8X16[chr*16+4], F8X16[chr*16+5], F8X16[chr*16+6], F8X16[chr*16+7],
                F8X16[chr*16+8], F8X16[chr*16+9], F8X16[chr*16+10], F8X16[chr*16+11], F8X16[chr*16+12], F8X16[chr*16+13], F8X16[chr*16+14], F8X16[chr*16+15]);  //显示内容
        else
            I2C_WriteData_Multiple(16, ~F8X16[chr*16], ~F8X16[chr*16+1], ~F8X16[chr*16+2], ~F8X16[chr*16+3], ~F8X16[chr*16+4], ~F8X16[chr*16+5], ~F8X16[chr*16+6],
                ~F8X16[chr*16+7], ~F8X16[chr*16+8], ~F8X16[chr*16+9], ~F8X16[chr*16+10], ~F8X16[chr*16+11], ~F8X16[chr*16+12], ~F8X16[chr*16+13], ~F8X16[chr*16+14], ~F8X16[chr*16+15]);  //显示内容
        return 0;
    }
    if (char_height == SIZE_12x24) {
        if (x+12 > SSD1315_WIDTH-1 || page+2 > SSD1315_HEIGHT/8-1) return -1;
        OLED_Set_Show_scope(x, x+11, page, page+2);//指定显示位置，注意12x24的字号会占据三个page
        if (!char_turn)
            I2C_WriteData_Multiple(36, F12X24[chr*36], F12X24[chr*36+1], F12X24[chr*36+2], F12X24[chr*36+3], F12X24[chr*36+4], F12X24[chr*36+5], F12X24[chr*36+6],
                F12X24[chr*36+7], F12X24[chr*36+8], F12X24[chr*36+9], F12X24[chr*36+10], F12X24[chr*36+11], F12X24[chr*36+12], F12X24[chr*36+13], F12X24[chr*36+14], F12X24[chr*36+15],
                F12X24[chr*36+16], F12X24[chr*36+17], F12X24[chr*36+18], F12X24[chr*36+19], F12X24[chr*36+20], F12X24[chr*36+21], F12X24[chr*36+22], F12X24[chr*36+23],
                F12X24[chr*36+24], F12X24[chr*36+25], F12X24[chr*36+26], F12X24[chr*36+27], F12X24[chr*36+28], F12X24[chr*36+29], F12X24[chr*36+30], F12X24[chr*36+31],
                F12X24[chr*36+32], F12X24[chr*36+33], F12X24[chr*36+34], F12X24[chr*36+35]);
        else
            I2C_WriteData_Multiple(36, ~F12X24[chr*36], ~F12X24[chr*36+1], ~F12X24[chr*36+2], ~F12X24[chr*36+3], ~F12X24[chr*36+4], ~F12X24[chr*36+5], ~F12X24[chr*36+6],
                ~F12X24[chr*36+7], ~F12X24[chr*36+8], ~F12X24[chr*36+9], ~F12X24[chr*36+10], ~F12X24[chr*36+11], ~F12X24[chr*36+12], ~F12X24[chr*36+13], ~F12X24[chr*36+14], ~F12X24[chr*36+15],
                ~F12X24[chr*36+16], ~F12X24[chr*36+17], ~F12X24[chr*36+18], ~F12X24[chr*36+19], ~F12X24[chr*36+20], ~F12X24[chr*36+21], ~F12X24[chr*36+22], ~F12X24[chr*36+23],
                ~F12X24[chr*36+24], ~F12X24[chr*36+25], ~F12X24[chr*36+26], ~F12X24[chr*36+27], ~F12X24[chr*36+28], ~F12X24[chr*36+29], ~F12X24[chr*36+30], ~F12X24[chr*36+31],
                ~F12X24[chr*36+32], ~F12X24[chr*36+33], ~F12X24[chr*36+34], ~F12X24[chr*36+35]);
        return 0;
    }
    if (char_height == SIZE_16x32) {
        if (x+16 > SSD1315_WIDTH-1 || page+3 > SSD1315_HEIGHT/8-1) return -1;
        OLED_Set_Show_scope(x, x+15, page, page+3);//指定显示位置，注意16x32的字号会占据四个page
        if (!char_turn)
            I2C_WriteData_Multiple(64, F16X32[chr*64], F16X32[chr*64+1], F16X32[chr*64+2], F16X32[chr*64+3], F16X32[chr*64+4], F16X32[chr*64+5], F16X32[chr*64+6],
                F16X32[chr*64+7], F16X32[chr*64+8], F16X32[chr*64+9], F16X32[chr*64+10], F16X32[chr*64+11], F16X32[chr*64+12], F16X32[chr*64+13], F16X32[chr*64+14], F16X32[chr*64+15],
                F16X32[chr*64+16], F16X32[chr*64+17], F16X32[chr*64+18], F16X32[chr*64+19], F16X32[chr*64+20], F16X32[chr*64+21], F16X32[chr*64+22], F16X32[chr*64+23],
                F16X32[chr*64+24], F16X32[chr*64+25], F16X32[chr*64+26], F16X32[chr*64+27], F16X32[chr*64+28], F16X32[chr*64+29], F16X32[chr*64+30], F16X32[chr*64+31],
                F16X32[chr*64+32], F16X32[chr*64+33], F16X32[chr*64+34], F16X32[chr*64+35], F16X32[chr*64+36], F16X32[chr*64+37], F16X32[chr*64+38], F16X32[chr*64+39],
                F16X32[chr*64+40], F16X32[chr*64+41], F16X32[chr*64+42], F16X32[chr*64+43], F16X32[chr*64+44], F16X32[chr*64+45], F16X32[chr*64+46], F16X32[chr*64+47],
                F16X32[chr*64+48], F16X32[chr*64+49], F16X32[chr*64+50], F16X32[chr*64+51], F16X32[chr*64+52], F16X32[chr*64+53], F16X32[chr*64+54], F16X32[chr*64+55],
                F16X32[chr*64+56], F16X32[chr*64+57], F16X32[chr*64+58], F16X32[chr*64+59], F16X32[chr*64+60], F16X32[chr*64+61], F16X32[chr*64+62], F16X32[chr*64+63]);
        else
            I2C_WriteData_Multiple(64, ~F16X32[chr*64], ~F16X32[chr*64+1], ~F16X32[chr*64+2], ~F16X32[chr*64+3], ~F16X32[chr*64+4], ~F16X32[chr*64+5], ~F16X32[chr*64+6],
                ~F16X32[chr*64+7], ~F16X32[chr*64+8], ~F16X32[chr*64+9], ~F16X32[chr*64+10], ~F16X32[chr*64+11], ~F16X32[chr*64+12], ~F16X32[chr*64+13], ~F16X32[chr*64+14], ~F16X32[chr*64+15],
                ~F16X32[chr*64+16], ~F16X32[chr*64+17], ~F16X32[chr*64+18], ~F16X32[chr*64+19], ~F16X32[chr*64+20], ~F16X32[chr*64+21], ~F16X32[chr*64+22], ~F16X32[chr*64+23],
                ~F16X32[chr*64+24], ~F16X32[chr*64+25], ~F16X32[chr*64+26], ~F16X32[chr*64+27], ~F16X32[chr*64+28], ~F16X32[chr*64+29], ~F16X32[chr*64+30], ~F16X32[chr*64+31],
                ~F16X32[chr*64+32], ~F16X32[chr*64+33], ~F16X32[chr*64+34], ~F16X32[chr*64+35], ~F16X32[chr*64+36], ~F16X32[chr*64+37], ~F16X32[chr*64+38], ~F16X32[chr*64+39],
                ~F16X32[chr*64+40], ~F16X32[chr*64+41], ~F16X32[chr*64+42], ~F16X32[chr*64+43], ~F16X32[chr*64+44], ~F16X32[chr*64+45], ~F16X32[chr*64+46], ~F16X32[chr*64+47],
                ~F16X32[chr*64+48], ~F16X32[chr*64+49], ~F16X32[chr*64+50], ~F16X32[chr*64+51], ~F16X32[chr*64+52], ~F16X32[chr*64+53], ~F16X32[chr*64+54], ~F16X32[chr*64+55],
                ~F16X32[chr*64+56], ~F16X32[chr*64+57], ~F16X32[chr*64+58], ~F16X32[chr*64+59], ~F16X32[chr*64+60], ~F16X32[chr*64+61], ~F16X32[chr*64+62], ~F16X32[chr*64+63]);
        return 0;
    }
    if (char_height == SIZE_24x48) {
        if (x+24 > SSD1315_WIDTH-1 || page+6 > SSD1315_HEIGHT/8-1) return -1;
        OLED_Set_Show_scope(x, x+23, page, page+4);//指定显示位置，注意24x48的字号会占据五个page
        if (!char_turn)
            I2C_WriteData_Multiple(144, F24X48[chr*144], F24X48[chr*144+1], F24X48[chr*144+2], F24X48[chr*144+3], F24X48[chr*144+4], F24X48[chr*144+5], F24X48[chr*144+6],
                F24X48[chr*144+7], F24X48[chr*144+8], F24X48[chr*144+9], F24X48[chr*144+10], F24X48[chr*144+11], F24X48[chr*144+12], F24X48[chr*144+13], F24X48[chr*144+14], F24X48[chr*144+15],
                F24X48[chr*144+16], F24X48[chr*144+17], F24X48[chr*144+18], F24X48[chr*144+19], F24X48[chr*144+20], F24X48[chr*144+21], F24X48[chr*144+22], F24X48[chr*144+23],
                F24X48[chr*144+24], F24X48[chr*144+25], F24X48[chr*144+26], F24X48[chr*144+27], F24X48[chr*144+28], F24X48[chr*144+29], F24X48[chr*144+30], F24X48[chr*144+31],
                F24X48[chr*144+32], F24X48[chr*144+33], F24X48[chr*144+34], F24X48[chr*144+35], F24X48[chr*144+36], F24X48[chr*144+37], F24X48[chr*144+38], F24X48[chr*144+39],
                F24X48[chr*144+40], F24X48[chr*144+41], F24X48[chr*144+42], F24X48[chr*144+43], F24X48[chr*144+44], F24X48[chr*144+45], F24X48[chr*144+46], F24X48[chr*144+47],
                F24X48[chr*144+48], F24X48[chr*144+49], F24X48[chr*144+50], F24X48[chr*144+51], F24X48[chr*144+52], F24X48[chr*144+53], F24X48[chr*144+54], F24X48[chr*144+55],
                F24X48[chr*144+56], F24X48[chr*144+57], F24X48[chr*144+58], F24X48[chr*144+59], F24X48[chr*144+60], F24X48[chr*144+61], F24X48[chr*144+62], F24X48[chr*144+63],
                F24X48[chr*144+64], F24X48[chr*144+65], F24X48[chr*144+66], F24X48[chr*144+67], F24X48[chr*144+68], F24X48[chr*144+69], F24X48[chr*144+70], F24X48[chr*144+71],
                F24X48[chr*144+72], F24X48[chr*144+73], F24X48[chr*144+74], F24X48[chr*144+75], F24X48[chr*144+76], F24X48[chr*144+77], F24X48[chr*144+78], F24X48[chr*144+79],
                F24X48[chr*144+80], F24X48[chr*144+81], F24X48[chr*144+82], F24X48[chr*144+83], F24X48[chr*144+84], F24X48[chr*144+85], F24X48[chr*144+86], F24X48[chr*144+87],
                F24X48[chr*144+88], F24X48[chr*144+89], F24X48[chr*144+90], F24X48[chr*144+91], F24X48[chr*144+92], F24X48[chr*144+93], F24X48[chr*144+94], F24X48[chr*144+95],
                F24X48[chr*144+96], F24X48[chr*144+97], F24X48[chr*144+98], F24X48[chr*144+99], F24X48[chr*144+100], F24X48[chr*144+101], F24X48[chr*144+102], F24X48[chr*144+103],
                F24X48[chr*144+104], F24X48[chr*144+105], F24X48[chr*144+106], F24X48[chr*144+107], F24X48[chr*144+108], F24X48[chr*144+109], F24X48[chr*144+110], F24X48[chr*144+111],
                F24X48[chr*144+112], F24X48[chr*144+113], F24X48[chr*144+114], F24X48[chr*144+115], F24X48[chr*144+116], F24X48[chr*144+117], F24X48[chr*144+118], F24X48[chr*144+119],
                F24X48[chr*144+120], F24X48[chr*144+121], F24X48[chr*144+122], F24X48[chr*144+123], F24X48[chr*144+124], F24X48[chr*144+125], F24X48[chr*144+126], F24X48[chr*144+127],
                F24X48[chr*144+128], F24X48[chr*144+129], F24X48[chr*144+130], F24X48[chr*144+131], F24X48[chr*144+132], F24X48[chr*144+133], F24X48[chr*144+134], F24X48[chr*144+135],
                F24X48[chr*144+136], F24X48[chr*144+137], F24X48[chr*144+138], F24X48[chr*144+139], F24X48[chr*144+140], F24X48[chr*144+141], F24X48[chr*144+142], F24X48[chr*144+143]);
        else
            I2C_WriteData_Multiple(144, ~F24X48[chr*144], ~F24X48[chr*144+1], ~F24X48[chr*144+2], ~F24X48[chr*144+3], ~F24X48[chr*144+4], ~F24X48[chr*144+5], ~F24X48[chr*144+6],
                ~F24X48[chr*144+7], ~F24X48[chr*144+8], ~F24X48[chr*144+9], ~F24X48[chr*144+10], ~F24X48[chr*144+11], ~F24X48[chr*144+12], ~F24X48[chr*144+13], ~F24X48[chr*144+14], ~F24X48[chr*144+15],
                ~F24X48[chr*144+16], ~F24X48[chr*144+17], ~F24X48[chr*144+18], ~F24X48[chr*144+19], ~F24X48[chr*144+20], ~F24X48[chr*144+21], ~F24X48[chr*144+22], ~F24X48[chr*144+23],
                ~F24X48[chr*144+24], ~F24X48[chr*144+25], ~F24X48[chr*144+26], ~F24X48[chr*144+27], ~F24X48[chr*144+28], ~F24X48[chr*144+29], ~F24X48[chr*144+30], ~F24X48[chr*144+31],
                ~F24X48[chr*144+32], ~F24X48[chr*144+33], ~F24X48[chr*144+34], ~F24X48[chr*144+35], ~F24X48[chr*144+36], ~F24X48[chr*144+37], ~F24X48[chr*144+38], ~F24X48[chr*144+39],
                ~F24X48[chr*144+40], ~F24X48[chr*144+41], ~F24X48[chr*144+42], ~F24X48[chr*144+43], ~F24X48[chr*144+44], ~F24X48[chr*144+45], ~F24X48[chr*144+46], ~F24X48[chr*144+47],
                ~F24X48[chr*144+48], ~F24X48[chr*144+49], ~F24X48[chr*144+50], ~F24X48[chr*144+51], ~F24X48[chr*144+52], ~F24X48[chr*144+53], ~F24X48[chr*144+54], ~F24X48[chr*144+55],
                ~F24X48[chr*144+56], ~F24X48[chr*144+57], ~F24X48[chr*144+58], ~F24X48[chr*144+59], ~F24X48[chr*144+60], ~F24X48[chr*144+61], ~F24X48[chr*144+62], ~F24X48[chr*144+63],
                ~F24X48[chr*144+64], ~F24X48[chr*144+65], ~F24X48[chr*144+66], ~F24X48[chr*144+67], ~F24X48[chr*144+68], ~F24X48[chr*144+69], ~F24X48[chr*144+70], ~F24X48[chr*144+71],
                ~F24X48[chr*144+72], ~F24X48[chr*144+73], ~F24X48[chr*144+74], ~F24X48[chr*144+75], ~F24X48[chr*144+76], ~F24X48[chr*144+77], ~F24X48[chr*144+78], ~F24X48[chr*144+79],
                ~F24X48[chr*144+80], ~F24X48[chr*144+81], ~F24X48[chr*144+82], ~F24X48[chr*144+83], ~F24X48[chr*144+84], ~F24X48[chr*144+85], ~F24X48[chr*144+86], ~F24X48[chr*144+87],
                ~F24X48[chr*144+88], ~F24X48[chr*144+89], ~F24X48[chr*144+90], ~F24X48[chr*144+91], ~F24X48[chr*144+92], ~F24X48[chr*144+93], ~F24X48[chr*144+94], ~F24X48[chr*144+95],
                ~F24X48[chr*144+96], ~F24X48[chr*144+97], ~F24X48[chr*144+98], ~F24X48[chr*144+99], ~F24X48[chr*144+100], ~F24X48[chr*144+101], ~F24X48[chr*144+102], ~F24X48[chr*144+103],
                ~F24X48[chr*144+104], ~F24X48[chr*144+105], ~F24X48[chr*144+106], ~F24X48[chr*144+107], ~F24X48[chr*144+108], ~F24X48[chr*144+109], ~F24X48[chr*144+110], ~F24X48[chr*144+111],
                ~F24X48[chr*144+112], ~F24X48[chr*144+113], ~F24X48[chr*144+114], ~F24X48[chr*144+115], ~F24X48[chr*144+116], ~F24X48[chr*144+117], ~F24X48[chr*144+118], ~F24X48[chr*144+119],
                ~F24X48[chr*144+120], ~F24X48[chr*144+121], ~F24X48[chr*144+122], ~F24X48[chr*144+123], ~F24X48[chr*144+124], ~F24X48[chr*144+125], ~F24X48[chr*144+126], ~F24X48[chr*144+127],
                ~F24X48[chr*144+128], ~F24X48[chr*144+129], ~F24X48[chr*144+130], ~F24X48[chr*144+131], ~F24X48[chr*144+132], ~F24X48[chr*144+133], ~F24X48[chr*144+134], ~F24X48[chr*144+135],
                ~F24X48[chr*144+136], ~F24X48[chr*144+137], ~F24X48[chr*144+138], ~F24X48[chr*144+139], ~F24X48[chr*144+140], ~F24X48[chr*144+141], ~F24X48[chr*144+142], ~F24X48[chr*144+143]);
        return 0;
    }
    return -1;  //如果没在上面return，则判断未显示正确，则返回-1
}

//------------------------------------------------------------------------------------------------------
// 函数简介         OLED显示字符串
// 使用示例         OLED_Show_String(0, 0, "Hello World", SIZE_8x16, 0);
// 参数介绍         x                   显示横坐标
// 参数介绍         page                显示页数
// 参数介绍         chr                 待显示字符
// 参数介绍         char_height         显示字符的高度，SIZE_6x8为6*8,SIZE_8x16为8*16,SIZE_24x48为24*48
// 参数介绍         char_turn           反转显示，0不反转，1反转
// 返回参数         (int)               0-正常 -1-异常
// 备注信息         不可以换行显示
//------------------------------------------------------------------------------------------------------
int OLED_Show_String(uint8_t x, uint8_t page,const char *str, font_size_t char_height, uint8_t char_turn) {
    assert(str);
    int i = 0;
    while (str[i] != '\0') {
        if (OLED_Show_Char(x, page, str[i], char_height, char_turn) == 0) {
            i++;
            x += (char_height == 8) ? 6 : 8;
        }
        else
            return -1;
    }
    return 0;
}

//------------------------------------------------------------------------------------------------------
// 函数简介         有符号整形转字符串
// 使用示例
// 参数介绍         *chr                输出字符串容器
// 参数介绍         num                 输入有符号整形
// 返回参数         (int)               0-正常 -1-异常
// 备注信息         原理：判断并处理负数->使用%10运算倒序分割提取数字->每个数字加'0'转成ASCII码存入目标数组
//------------------------------------------------------------------------------------------------------
static int func_int_to_str(char *chr, int num) {
    // assert(chr != NULL);                                //只要表达式为非零就通过,详细看他自己的宏定义
    int temp_num = 0;                         //数字缓冲区
    uint8_t index_bit = 0;                      //数字位数索引
    int *temp_clip_num = calloc(32, sizeof(int));   //数字切片缓存区，32个位置

    if (NULL == chr)                            //Yoda条件，常量在变量左边
        return -1;                              //之所以不会跑进来是因为前面已经assert(chr)了，而且由于assert是个宏定义，所以此时编译器会认为chr一定非空
    if (num < 0) {
        *chr++ = '-';                           //高级写法，先赋值再地址++自增
        num = -num;                             //聪明写法，比abs()更快
    }
    else if (num == 0){
        *chr++ = '0';
        return 0;
    }

    while (num != 0) {     //倒序分割提取数字
        temp_num = num % 10;
        temp_clip_num[index_bit++] = temp_num;      //注意此时index_bit比最后一位数字要多往后走了一次，所以下面用--index_bit而不是index_bit--
        num /= 10;
    }

    while (index_bit != 0) {
        *chr++ = '0' + temp_clip_num[--index_bit];              //数字转ASCII码,自增和自减是最后计算的；由上一步可知index_bit比数字位大1，要先减再计算；++--是最高级的运算，后++意味着先返回chr之后再自增，前++则先chr自增后再返回值；等效于*（chr++）
    }
    free(temp_clip_num);
    return 0;
}
//------------------------------------------------------------------------------------------------------
// 函数简介         OLED显示有符号整数（符号会占一位）
// 使用示例         OLED_Show_int(52, 0, -123, 2, SIZE_8x16, 1);
// 参数介绍         x                   显示横坐标
// 参数介绍         page                显示页数
// 参数介绍         num                 待显示数字
// 参数介绍         len                 待显示数字的长度
// 参数介绍         char_height         显示字符的高度，SIZE_6x8为6*8,SIZE_8x16为8*16,SIZE_24x48为24*48
// 参数介绍         char_turn           反转显示，0不反转，1反转
// 返回参数         (int)               0-正常 -1-异常
// 备注信息         不可以换行显示
//------------------------------------------------------------------------------------------------------
int OLED_Show_int(uint8_t x, uint8_t y, int num, uint8_t len, font_size_t char_height, uint8_t char_turn) {
    assert(x < SSD1315_WIDTH);
    assert(y < SSD1315_HEIGHT);
    assert(len > 0);    //不满足条件直接终止主函数
    // assert(len <= SSD1315_WIDTH / 8);

    char *chr_num= calloc(len + 2, sizeof(char));  //多一位给负号、再多一位给\0; 全初始化为'\0'，其实就是0
    int k = 1;                                  //显示位数不足的系数
    memset(chr_num, ' ', len + 1);            //初始化字符串容器为空

    if (num < 0) len--;         //数字＜0则直接削减一个显示位用于补给负号。用来固定显示位，只能在len的区间内显示
    for (; len > 0; len--) {                    //用来计算余数显示 123 显示 2 位则应该显示 23(逐飞说的)
        k *= 10;        //膨胀神k
    }
    num = num % k;      //得到限制显示位数之后的数字，正负不影响

    func_int_to_str(chr_num, num);          //int转换成字符串

    OLED_Show_String(x, y, chr_num, char_height, char_turn);

    free(chr_num);
    return 0;
}

//------------------------------------------------------------------------------------------------------
// 函数简介         无符号整形转字符串
// 使用示例
// 参数介绍         *chr                输出字符串容器
// 参数介绍         num                 输入无符号整形
// 返回参数         (int)               0-正常 -1-异常
// 备注信息
//------------------------------------------------------------------------------------------------------
static int func_uint_to_str(char *chr, uint32_t num) {
    // assert(chr != NULL);
    uint32_t temp_num = 0;                         //数字缓冲区
    uint8_t index_bit = 0;                      //数字切片位数索引
    uint32_t *temp_clip_num = calloc(32, sizeof(uint32_t));   //数字切片缓存区，32个位置

    if (NULL == chr)                            //Yoda条件，常量在变量左边
        return -1;      //之所以不会跑进来是因为前面已经assert(chr)了，而且由于assert是个宏定义，所以此时编译器会认为chr一定非空

    if (num == 0){
        *chr++ = '0';
        return 0;
    }

    while (num != 0) {     //倒序分割提取数字
        temp_num = num % 10;
        temp_clip_num[index_bit++] = temp_num;      //注意此时index_bit比最后一位数字要多往后走了一次，所以下面用--index_bit而不是index_bit--
        num /= 10;
    }

    while (index_bit != 0) {
        *chr++ = '0' + temp_clip_num[--index_bit];              //数字转ASCII码,自增和自减是最后计算的；由上一步可知index_bit比数字位大1，要先减再计算；++--是最高级的运算，后++意味着先返回chr之后再自增，前++则先chr自增后再返回值；等效于*（chr++）
    }
    free(temp_clip_num);
    return 0;
}
//------------------------------------------------------------------------------------------------------
// 函数简介         OLED显示无符号整数
// 使用示例         OLED_Show_uint(52, 0, 123, 3, SIZE_8x16, 1);
// 参数介绍         x                   显示横坐标
// 参数介绍         page                显示页数
// 参数介绍         num                 待显示数字
// 参数介绍         len                 待显示数字的长度
// 参数介绍         char_height         显示字符的高度，SIZE_6x8为6*8,SIZE_8x16为8*16,SIZE_24x48为24*48
// 参数介绍         char_turn           反转显示，0不反转，1反转
// 返回参数         (int)               0-正常 -1-异常
// 备注信息         不可以换行显示
//------------------------------------------------------------------------------------------------------
int OLED_Show_uint(uint8_t x, uint8_t y, uint32_t num, uint8_t len, font_size_t char_height, uint8_t char_turn) {
    assert(x < SSD1315_WIDTH);
    assert(y < SSD1315_HEIGHT);
    assert(len > 0);
    // assert(len <= SSD1315_WIDTH / 8);

    char *chr_num= calloc(len + 1, sizeof(char));  //多一位给\0; 全初始化为'\0'，其实就是0
    int k = 1;                                  //显示位数不足的系数
    memset(chr_num, ' ', len);            //初始化字符串容器为空,保留最后一位为\0

    for (; len > 0; len--) {                    //用来计算余数显示 123 显示 2 位则应该显示 23(逐飞说的)
        k *= 10;    //膨胀
    }
    num = num % k;      //得到限制显示位数之后的数字

    func_uint_to_str(chr_num, num);          //int转换成字符串

    OLED_Show_String(x, y, chr_num, char_height, char_turn);

    free(chr_num);
    return 0;
}

//------------------------------------------------------------------------------------------------------
// 函数简介         有符号浮点数转字符串
// 使用示例
// 参数介绍         *chr                输出字符串容器
// 参数介绍         num                 输入有符号浮点数
// 参数介绍         bit_save            需要保留的小数位数
// 返回参数         (int)               0-正常 -1-异常
// 备注信息
//------------------------------------------------------------------------------------------------------
static int func_float_to_str(char *chr, float num, uint8_t bit_save) {
    // assert(chr != NULL);                                //只要表达式为非零就通过,详细看他自己的宏定义
    uint32_t k = 1;
    for (int i = bit_save; i > 0; i--) {
        k *= 10;                //小数保留系数
    }
    //整数部分初始化
    int temp_part_font = (int)num; //整数部分

    if (NULL == chr)                            //Yoda条件，常量在变量左边
        return -1;                              //之所以不会跑进来是因为前面已经assert(chr)了，而且由于assert是个宏定义，所以此时编译器会认为chr一定非空
    if (num < 0) {
        *chr++ = '-';                           //高级写法，先赋值再地址++自增
        temp_part_font = -temp_part_font;                             //聪明写法，比abs()更快
        num = -num;
    }
    else if (num == 0){         //特殊情况，为0的判断
        *chr++ = '0';
        if (bit_save != 0) {
            *chr++ = '.';
            for (; bit_save > 0; bit_save--) {
                *chr++ = '0';
            }
        }
        return 0;
    }
    //这里要用到正的整数部分，所以初始化滞后在这里
    int temp_part_last = (int)((num - (float)temp_part_font) * (float)k); //小数部分
    int temp_num = 0;                         //数字缓冲区
    uint8_t index_bit = 0;                      //数字位数索引
    uint8_t index_point = 0;
    int *temp_clip_num = calloc(32, sizeof(int));   //数字切片缓存区，32个位置,单个数字是整形，所以依然用int

    while (bit_save--) {       //倒序分割提取小数部分(必须先小数后整数）
        temp_num = temp_part_last % 10;
        temp_clip_num[index_bit++] = temp_num;
        temp_part_last /= 10;
    }
    index_point = index_bit++;      //标记小数点位置
    while (temp_part_font != 0) {     //倒序分割提取整数部分
        temp_num = temp_part_font % 10;
        temp_clip_num[index_bit++] = temp_num;      //注意此时index_bit比最后一位数字要多往后走了一次，所以下面用--index_bit而不是index_bit--
        temp_part_font /= 10;       //整数这么用才是去最低位
    }

    while (index_bit != 0) {
        *chr++ = '0' + temp_clip_num[--index_bit];              //数字转ASCII码,自增和自减是最后计算的；由上一步可知index_bit比数字位大1，要先减再计算；++--是最高级的运算，后++意味着先返回chr之后再自增，前++则先chr自增后再返回值；等效于*（chr++）
        if (index_bit - 1 == index_point) {
            *chr++ = '.';
            index_bit--;
        }
    }
    free(temp_clip_num);
    return 0;
}

//------------------------------------------------------------------------------------------------------
// 函数简介         OLED显示有符号浮点数
// 使用示例         OLED_Show_float(8, 4, -13.01f, 3, 3, SIZE_6x8, 1);
// 参数介绍         x                   显示横坐标
// 参数介绍         page                显示页数
// 参数介绍         num                 待显示数字
// 参数介绍         len_int             整数部分的长度
// 参数介绍         len_float           小数部分的长度
// 参数介绍         char_height         显示字符的高度，SIZE_6x8为6*8,SIZE_8x16为8*16,SIZE_24x48为24*48
// 参数介绍         char_turn           反转显示，0不反转，1反转
// 返回参数         (int)               0-正常 -1-异常
// 备注信息         不可以换行显示；无四舍五入的功能; 注意len_int包含负号位
//------------------------------------------------------------------------------------------------------
int OLED_Show_float(uint8_t x, uint8_t y, float num, uint8_t len_int, uint8_t len_float, font_size_t char_height, uint8_t char_turn) {
    assert(x < SSD1315_WIDTH);
    assert(y < SSD1315_HEIGHT);
    assert(len_int > 0);    //不满足条件直接终止主函数
    assert(len_float > 0);
    // assert(len <= SSD1315_WIDTH / 8);
    // uint32_t k = 1;
    // for (uint8_t i = len_float; i > 0; i--) {
    //     k *= 10;                //小数保留系数
    // }

    int num_int = (int)num;                     //整数部分择出来备用
    float num_float = num-(float)num_int;       //小数部分择出来备用
    char *chr_num= calloc(len_int + len_float + 3, sizeof(char));  //多一位给负号、再多一位给\0,再多一位给小数点; 全初始化为'\0'，其实就是0
    int k = 1;                                  //显示位数不足的系数(借用一下上面的k)
    memset(chr_num, ' ', len_int + 1);            //初始化字符串容器为空

    if (num < 0) len_int--;         //数字＜0则直接削减一个显示位用于补给负号。用来固定显示位，只能在len_int的区间内显示
    for (; len_int > 0; len_int--) {                    //用来计算余数显示 123 显示 2 位则应该显示 23(逐飞说的)
        k *= 10;        //膨胀神k，用来得到限制显示位置之后的值
    }
    num_int = num_int % k;      //得到限制显示位数之后的整数部分，正负不影响
    num = (float)num_int + num_float;   //合成大西瓜

    func_float_to_str(chr_num, num, len_float);          //int转换成字符串
    OLED_Show_String(x, y, chr_num, char_height, char_turn);

    free(chr_num);
    return 0;
}

//------------------------------------------------------------------------------------------------------
// 函数简介         OLED显示汉字
// 使用示例           OLED_Show_Chinese_Multiple(76, 0, SIZE_CHA_16x16, Cha, 1, 3, 0, 1, 2);
// 参数介绍         x                   起始横坐标
// 参数介绍         page                起始页
// 参数介绍         size                汉字高度，正方形，16就是16*16,8就是8*8，支持
// 参数介绍         *Cha                字符数组
// 参数介绍         char_turn           反转显示，0不反转，1反转
// 参数介绍         count_Chinese       显示几个数字
// 参数介绍         ...                 数组内汉字序号
// 返回参数         (int)               0-正常 -1-异常
// 备注信息         不可以换行显示；无四舍五入的功能
// 备注信息         和逐飞的开源库不同，我这个是活字印刷式用法，逐飞的是盖章式用法
//------------------------------------------------------------------------------------------------------
int OLED_Show_Chinese_Multiple(uint8_t x, uint8_t page, const font_size_cha_t size, const uint8_t Cha[][32], uint8_t char_turn, uint8_t count_Chinese, ...) {
    assert(x < SSD1315_WIDTH);
    assert(page < 8);
    assert(x+(size*count_Chinese)<SSD1315_WIDTH);
    assert(size/8+page<=8);
    va_list args;
    va_start(args, count_Chinese);
    uint8_t index = 0;

    for (int i=0; i < count_Chinese; i++) {
        index = (uint8_t)va_arg(args, int);  //通过...传参会导致“默认参数提升”，char和short升级为int，float升级为double
        //设置显示范围，可多行
        OLED_Set_Show_scope(x+(size-1)*i, x+size-1+(size-1)*i, page, size/8-1+page);
        if (SIZE_CHA_16x16 == size) {
            if (!char_turn) {
                I2C_WriteData_Multiple(32, Cha[index][0], Cha[index][1], Cha[index][2], Cha[index][3], Cha[index][4], Cha[index][5], Cha[index][6], Cha[index][7], Cha[index][8], Cha[index][9], Cha[index][10], Cha[index][11], Cha[index][12], Cha[index][13], Cha[index][14], Cha[index][15],
                    Cha[index][16], Cha[index][17], Cha[index][18], Cha[index][19], Cha[index][20], Cha[index][21], Cha[index][22], Cha[index][23], Cha[index][24], Cha[index][25], Cha[index][26], Cha[index][27], Cha[index][28], Cha[index][29], Cha[index][30], Cha[index][31]);
            } else {
                I2C_WriteData_Multiple(32, ~Cha[index][0], ~Cha[index][1], ~Cha[index][2], ~Cha[index][3], ~Cha[index][4], ~Cha[index][5], ~Cha[index][6], ~Cha[index][7], ~Cha[index][8], ~Cha[index][9], ~Cha[index][10], ~Cha[index][11], ~Cha[index][12], ~Cha[index][13], ~Cha[index][14], ~Cha[index][15],
                    ~Cha[index][16], ~Cha[index][17], ~Cha[index][18], ~Cha[index][19], ~Cha[index][20], ~Cha[index][21], ~Cha[index][22], ~Cha[index][23], ~Cha[index][24], ~Cha[index][25], ~Cha[index][26], ~Cha[index][27], ~Cha[index][28], ~Cha[index][29], ~Cha[index][30], ~Cha[index][31]);
            }
        } if (SIZE_CHA_8x8 == size) {
            if (!char_turn) {
                I2C_WriteData_Multiple(16, Cha[index][0], Cha[index][1], Cha[index][2], Cha[index][3], Cha[index][4], Cha[index][5], Cha[index][6], Cha[index][7], Cha[index][8], Cha[index][9], Cha[index][10], Cha[index][11], Cha[index][12], Cha[index][13], Cha[index][14], Cha[index][15]);
            } else {
                I2C_WriteData_Multiple(16, ~Cha[index][0], ~Cha[index][1], ~Cha[index][2], ~Cha[index][3], ~Cha[index][4], ~Cha[index][5], ~Cha[index][6], ~Cha[index][7], ~Cha[index][8], ~Cha[index][9], ~Cha[index][10], ~Cha[index][11], ~Cha[index][12], ~Cha[index][13], ~Cha[index][14], ~Cha[index][15]);
            }
        } if (SIZE_CHA_24x24 == size) {
            if (!char_turn) {
                I2C_WriteData_Multiple(72, Cha[index][0], Cha[index][1], Cha[index][2], Cha[index][3], Cha[index][4], Cha[index][5], Cha[index][6], Cha[index][7], Cha[index][8], Cha[index][9],
                    Cha[index][10], Cha[index][11], Cha[index][12], Cha[index][13], Cha[index][14], Cha[index][15], Cha[index][16], Cha[index][17], Cha[index][18], Cha[index][19], Cha[index][20],
                    Cha[index][21], Cha[index][22], Cha[index][23], Cha[index][24], Cha[index][25], Cha[index][26], Cha[index][27], Cha[index][28], Cha[index][29], Cha[index][30], Cha[index][31],
                    Cha[index][32], Cha[index][33], Cha[index][34], Cha[index][35], Cha[index][36], Cha[index][37], Cha[index][38], Cha[index][39], Cha[index][40], Cha[index][41], Cha[index][42],
                    Cha[index][43], Cha[index][44], Cha[index][45], Cha[index][46], Cha[index][47], Cha[index][48], Cha[index][49], Cha[index][50], Cha[index][51], Cha[index][52], Cha[index][53],
                    Cha[index][54], Cha[index][55], Cha[index][56], Cha[index][57], Cha[index][58], Cha[index][59], Cha[index][60], Cha[index][61], Cha[index][62], Cha[index][63], Cha[index][64],
                    Cha[index][65], Cha[index][66], Cha[index][67], Cha[index][68], Cha[index][69], Cha[index][70], Cha[index][71]);
            } else {
                I2C_WriteData_Multiple(72, ~Cha[index][0], ~Cha[index][1], ~Cha[index][2], ~Cha[index][3], ~Cha[index][4], ~Cha[index][5], ~Cha[index][6], ~Cha[index][7], ~Cha[index][8], ~Cha[index][9],
                     ~Cha[index][10], ~Cha[index][11], ~Cha[index][12], ~Cha[index][13], ~Cha[index][14], ~Cha[index][15], ~Cha[index][16], ~Cha[index][17], ~Cha[index][18], ~Cha[index][19], ~Cha[index][20],
                     ~Cha[index][21],~Cha[index][22], ~Cha[index][23], ~Cha[index][24], ~Cha[index][25], ~Cha[index][26], ~Cha[index][27], ~Cha[index][28], ~Cha[index][29], ~Cha[index][30], ~Cha[index][31],
                     ~Cha[index][32], ~Cha[index][33], ~Cha[index][34], ~Cha[index][35], ~Cha[index][36], ~Cha[index][37], ~Cha[index][38], ~Cha[index][39], ~Cha[index][40], ~Cha[index][41], ~Cha[index][42],
                     ~Cha[index][43], ~Cha[index][44], ~Cha[index][45], ~Cha[index][46], ~Cha[index][47], ~Cha[index][48], ~Cha[index][49], ~Cha[index][50], ~Cha[index][51], ~Cha[index][52], ~Cha[index][53],
                     ~Cha[index][54], ~Cha[index][55], ~Cha[index][56], ~Cha[index][57], ~Cha[index][58], ~Cha[index][59], ~Cha[index][60], ~Cha[index][61], ~Cha[index][62], ~Cha[index][63], ~Cha[index][64],
                     ~Cha[index][65], ~Cha[index][66], ~Cha[index][67], ~Cha[index][68], ~Cha[index][69], ~Cha[index][70], ~Cha[index][71]);
            }
            }
    }
    va_end(args);
    return 0;
}

//------------------------------------------------------------------------------------------------------
// 函数简介         OLED显示.bmp图片
// 使用示例         OLED_Show_BMP(0, 0+31, 0, 0+3, BMP1, 1);
// 参数介绍         x                   起始横坐标
// 参数介绍         x_end               结束横坐标
// 参数介绍         page                起始页数（一页8行像素）
// 参数介绍         page_end            结束页数
// 参数介绍         char_turn           反转显示，0不反转，1反转
// 返回参数         (int)               0-正常 -1-异常
//
//------------------------------------------------------------------------------------------------------
int OLED_Show_BMP(uint8_t x, uint8_t x_end, uint8_t page, uint8_t page_end, uint8_t *bmp, uint8_t char_turn) {
    assert(x < SSD1315_WIDTH);      //因为是uint8，所以只需要判断上边界就好了，下边界会自动从255开始减
    assert(page_end < SSD1315_HEIGHT);
    assert(bmp);

    uint8_t x_delta = x_end - x + 1;            //确定长宽
    uint8_t page_delta = page_end - page + 1;
    uint8_t *temp_bmp = bmp;
    // uint8_t page_delta = ((y_end-y+1)%8 == 0) ? (y_end-y+1)/8 : (uint8_t)clip((y_end-y+1)/8+1, 0, 7);

    OLED_Set_Show_scope(x, x_end, page, page_end);//指定显示位置

    if (!char_turn)
        for (int i = x_delta * page_delta; i > 0; i--) {
            I2C_WriteData_Multiple(1, *temp_bmp++);
        }
    else
        for (int i = x_delta * page_delta; i>0; i--) {
            I2C_WriteData_Multiple(1, ~(*temp_bmp++));
        }
    return 0;

}